"""
微信视频号直播事件监听器 - 简化版，用于测试响应监听功能
"""
import asyncio
import json
import threading
import time
import os
import shutil
import concurrent.futures
from playwright.async_api import async_playwright
from app.utils.logger import Logger
from .wx_data_decoder import WXDataDecoder
import sys

class WXLiveEventListener(threading.Thread):
    """微信直播事件监听器，使用Playwright监听网络请求 - 简化版"""
    
    def __init__(self, config, event_handler):
        """
        初始化监听器
        
        @param config: WXLiveConfig 配置对象
        @param event_handler: WXLiveEventHandler 事件处理器
        """
        super().__init__()
        self.daemon = True
        self.config = config
        self.event_handler = event_handler
        self.logger = Logger.get_logger("wx_live_listener")
        
        # 设置调试模式
        self.debug_mode = True
        self.logger.info("启用调试模式，将显示更多日志信息")
        
        # 硬编码默认URL，避免依赖配置
        self.default_url = "https://channels.weixin.qq.com/platform/live/liveBuild"
        
        self.running = False
        self.browser = None  # 保存浏览器实例引用
        self.context = None  # 保存上下文实例引用
        self._stop_event = threading.Event()  # 添加停止事件
        self._loop = None  # 事件循环引用
        
        # 添加简单的事件缓存，用于去重
        self.recent_event_hashes = set()
        self.max_event_cache = 200  # 最大缓存事件数
        
    def run(self):
        """线程运行方法"""
        try:
            self.running = True
            # 在新线程中创建一个事件循环
            self._loop = asyncio.new_event_loop()
            asyncio.set_event_loop(self._loop)
            
            # 运行监听方法
            self._loop.run_until_complete(self.start_listening())
        except Exception as e:
            self.logger.error(f"监听线程发生错误: {str(e)}")
        finally:
            self.running = False
            self._loop = None
    
    def stop(self):
        """
        停止监听器
        """
        self.logger.info("正在停止监听器...")
        self._stop_event.set()
        self.running = False
        
    def is_stopped(self):
        """
        检查监听器是否已停止
        
        @return: 是否已停止
        """
        return self._stop_event.is_set()
    
    async def start_listening(self):
        """启动浏览器监听直播间事件"""
        self.logger.info("正在启动浏览器监听直播间...")
        
        # 重置停止事件
        self._stop_event.clear()
        
        # 添加保活标志
        self._keep_alive = True
        
        try:
            self.logger.info(f"使用URL: {self.default_url}")
            
            # 尝试查找Chrome浏览器的可执行文件
            executable_path = self._find_chrome_browser()
            
            # 创建Chrome浏览器实例
            async with async_playwright() as p:
                try:
                    # 根据是否有可执行路径决定启动方式
                    if executable_path:
                        self.logger.info(f"使用指定的Chrome可执行文件: {executable_path}")
                        self.browser = await p.chromium.launch(
                            headless=False,
                            executable_path=executable_path,
                            args=[
                                '--disable-setuid-sandbox',
                                '--window-size=1024,768',
                                '--disable-gpu',
                                '--ignore-https-errors',
                            ],
                        )
                    else:
                        self.logger.info("使用Playwright内置的Chromium浏览器")
                        self.browser = await p.chromium.launch(
                            headless=False,
                            args=[
                                '--disable-setuid-sandbox',
                                '--window-size=1024,768',
                                '--disable-gpu',
                                '--ignore-https-errors',
                            ],
                        )
                    self.logger.info("浏览器启动成功")
                    
                    # 创建简单上下文
                    self.logger.debug("创建浏览器上下文...")
                    self.context = await self.browser.new_context()
                    
                    # 绑定响应监听器
                    self.logger.debug("绑定响应监听器...")
                    try:
                        self.context.on("response", self._handle_response)
                        self.logger.debug("响应监听器绑定成功")
                    except Exception as e:
                        self.logger.error(f"绑定响应监听器失败: {str(e)}")
                        raise
                    
                    # 创建页面
                    self.logger.debug("创建新页面...")
                    page = await self.context.new_page()
                    
                    # 设置页面错误处理
                    page.on("pageerror", lambda error: self.logger.error(f"页面错误: {error}"))
                    page.on("crash", lambda: self.logger.error("页面崩溃"))
                    page.on("console", lambda msg: self._on_console_message(msg))
                    
                    # 设置页面关闭处理
                    page.on("close", lambda: self.logger.warning("页面被关闭，但监听器将继续运行"))
                    
                    # 设置页面超时
                    page.set_default_timeout(60000)  # 60秒
                    
                    # 导航到目标URL
                    target_url = self.default_url
                    self.logger.info(f"导航到页面: {target_url}")
                    try:
                        await page.goto(target_url)
                        self.logger.info("页面导航成功")
                        
                        # 测试响应监听器
                        self.logger.debug("测试响应监听器是否工作...")
                        await page.evaluate("""() => { 
                            console.log('发送测试请求...');
                            fetch('https://www.baidu.com').then(r => r.text()).then(text => {
                                console.log('测试请求成功，响应长度: ' + text.length);
                            });
                        }""")
                        
                        # 等待一段时间，确保响应被处理
                        await asyncio.sleep(3)
                        
                        # 添加保活代码，定期执行一些操作，防止浏览器自动关闭
                        keep_alive_task = asyncio.create_task(self._keep_browser_alive(page))
                        
                        # 使用无限循环等待方式
                        self.logger.info("监听器已启动，等待停止事件...")
                        while not self._stop_event.is_set():
                            await asyncio.sleep(1)
                            
                            # 检查浏览器是否仍然连接
                            if self.browser and not self._check_browser_connected():
                                break
                            
                            # 检查页面是否仍然存在
                            if self._is_page_closed(page):
                                break
                        
                        # 取消保活任务
                        self._keep_alive = False
                        if 'keep_alive_task' in locals():
                            keep_alive_task.cancel()
                        
                        self.logger.info("监听循环已退出")
                    
                    except Exception as e:
                        self.logger.error(f"导航到URL失败: {str(e)}")
                    
                    # 尝试关闭浏览器
                    await self._close_browser()
                
                except Exception as e:
                    self.logger.error(f"浏览器操作失败: {str(e)}")
                    import traceback
                    self.logger.error(f"异常堆栈: {traceback.format_exc()}")
                    
                    # 确保任何情况下都尝试关闭浏览器
                    await self._close_browser()
        
        except Exception as e:
            self.logger.error(f"启动监听器时出错: {str(e)}")
            import traceback
            self.logger.error(f"异常堆栈: {traceback.format_exc()}")
    
    def _find_chrome_browser(self):
        """
        查找可用的Chrome浏览器
        
        @return: Chrome浏览器可执行文件路径，如果未找到则返回None
        """
        # 获取应用根目录
        app_root = self._get_app_root()
        self.logger.debug(f"应用根目录: {app_root}")
        
        # 定义可能的Chrome浏览器路径
        search_paths = [
            # 1. chrome-win目录
            os.path.join(app_root, "chrome-win", "chrome.exe"),
            # 2. chrome-plugin目录
            os.path.join(app_root, "chrome-plugin"),
            # 3. browser目录
            os.path.join(app_root, "browser", "chrome.exe" if sys.platform == "win32" else "chrome"),
        ]
        
        # 添加系统Chrome路径
        if sys.platform == "win32":
            system_paths = [
                os.path.join(os.environ.get('PROGRAMFILES', 'C:\\Program Files'), 'Google\\Chrome\\Application\\chrome.exe'),
                os.path.join(os.environ.get('PROGRAMFILES(X86)', 'C:\\Program Files (x86)'), 'Google\\Chrome\\Application\\chrome.exe'),
                os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google\\Chrome\\Application\\chrome.exe')
            ]
            search_paths.extend(system_paths)
        elif sys.platform == "darwin":  # macOS
            search_paths.append('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome')
        elif sys.platform.startswith("linux"):
            search_paths.extend(['/usr/bin/google-chrome', '/usr/bin/chromium-browser'])
        
        # 查找Chrome浏览器
        chrome_paths = []
        
        for path in search_paths:
            # 处理目录情况
            if os.path.exists(path) and os.path.isdir(path):
                for root, dirs, files in os.walk(path):
                    for file in files:
                        if file.lower() == "chrome.exe":
                            chrome_paths.append(os.path.join(root, file))
                            self.logger.debug(f"在目录中找到Chrome: {os.path.join(root, file)}")
                            break
            # 处理文件情况
            elif os.path.exists(path) and os.path.isfile(path):
                chrome_paths.append(path)
                self.logger.debug(f"找到Chrome: {path}")
        
        # 使用找到的第一个Chrome路径
        if chrome_paths:
            return chrome_paths[0]
        
        self.logger.warning("未找到本地Chrome浏览器，将使用Playwright内置浏览器")
        return None
    
    def _get_app_root(self):
        """
        获取应用根目录
        
        @return: 应用根目录
        """
        if getattr(sys, 'frozen', False):
            # 打包环境
            return os.path.dirname(sys.executable)
        else:
            # 开发环境
            app_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
            # 检查是否需要添加tools-pc-v3目录
            if os.path.basename(app_root) != "tools-pc-v3":
                app_root = os.path.join(app_root, "tools-pc-v3")
            return app_root
    
    def _check_browser_connected(self):
        """
        检查浏览器是否仍然连接
        
        @return: 浏览器是否仍然连接
        """
        if not self.browser:
            return False
            
        try:
            is_connected = self.browser.is_connected()
            if not is_connected:
                self.logger.warning("浏览器已断开连接，退出监听")
                return False
            return True
        except Exception as e:
            self.logger.warning(f"检查浏览器连接状态时出错: {str(e)}")
            return False
    
    def _is_page_closed(self, page):
        """
        检查页面是否已关闭
        
        @param page: 页面对象
        @return: 页面是否已关闭
        """
        try:
            if page.is_closed():
                self.logger.warning("页面已关闭，退出监听")
                return True
            return False
        except Exception as e:
            self.logger.warning(f"检查页面状态时出错: {str(e)}")
            return True
    
    async def _close_browser(self):
        """
        关闭浏览器
        """
        if self.browser:
            try:
                self.logger.info("正在关闭浏览器...")
                await self.browser.close()
                self.logger.info("浏览器已关闭")
            except Exception as e:
                self.logger.error(f"关闭浏览器时出错: {str(e)}")
            finally:
                self.browser = None
                self.context = None
    
    async def _handle_response(self, response):
        """
        处理响应 - 与 wxlivespy 项目逻辑一致
        
        @param response: Response对象
        """
        try:
            # 检查是否需要跳过该响应
            if self._should_skip_response(response):
                return
                
            url = response.url
            self.logger.debug(f"处理响应: {url}")
            
            # 获取请求头和请求数据
            try:
                request = response.request
                request_headers = request.headers
                
                # 只处理POST请求
                if request.method != "POST":
                    return
                
                # 尝试获取请求的POST数据
                try:
                    request_post_data = request.post_data
                    if not request_post_data:
                        self.logger.debug("请求数据为空")
                        return
                        
                    try:
                        request_data = json.loads(request_post_data)
                    except json.JSONDecodeError:
                        self.logger.debug(f"请求数据不是有效的JSON: {request_post_data[:100]}...")
                        return
                except Exception as e:
                    self.logger.debug(f"获取请求POST数据失败: {str(e)}")
                    return
                
                # 获取响应数据
                try:
                    content_type = response.headers.get("content-type", "")
                    if "json" in content_type or "application/json" in content_type:
                        response_data = await response.json()
                    else:
                        self.logger.debug(f"响应不是JSON格式: {content_type}")
                        return
                except Exception as e:
                    self.logger.debug(f"解析响应JSON失败: {str(e)}")
                    return
                
                # 使用WXDataDecoder解码数据
                try:
                    decoded_data = WXDataDecoder.decode_data_from_response(
                        request_headers, request_data, response_data
                    )
                    
                    # 处理直播信息
                    if decoded_data and "live_info" in decoded_data and decoded_data["live_info"]:
                        self.event_handler.on_status_update(decoded_data["live_info"])
                    
                    # 处理事件 - 添加去重逻辑
                    if decoded_data and "events" in decoded_data and len(decoded_data["events"]) > 0:
                        # 过滤出唯一事件
                        unique_events = []
                        for event in decoded_data["events"]:
                            # 生成简单的事件哈希
                            event_str = f"{event.get('decoded_type', '')}-{event.get('nickname', '')}-{event.get('content', '')}"
                            event_hash = hash(event_str)
                            
                            # 检查是否已处理
                            if event_hash in self.recent_event_hashes:
                                self.logger.debug(f"跳过重复事件: {event_str}")
                                continue
                            
                            # 添加到已处理集合和唯一事件列表
                            self.recent_event_hashes.add(event_hash)
                            unique_events.append(event)
                        
                        # 限制集合大小
                        if len(self.recent_event_hashes) > self.max_event_cache:
                            self.logger.info(f"事件缓存已达到上限({self.max_event_cache})，清空缓存")
                            self.recent_event_hashes.clear()
                        
                        # 只有有唯一事件时才发送
                        if unique_events:
                            self.logger.info(f"获取到{len(unique_events)}个唯一事件，总事件数: {len(decoded_data['events'])}")
                            for idx, event in enumerate(unique_events):
                                self.logger.info(f"事件{idx+1}: 类型={event.get('decoded_type', '未知')}, "
                                              f"用户={event.get('nickname', '未知')}, "
                                              f"内容={event.get('content', '无内容')}")
                            
                            self.event_handler.on_events({"events": unique_events})
                        
                except Exception as e:
                    self.logger.error(f"解码数据失败: {str(e)}")
                    if self.debug_mode:
                        import traceback
                        self.logger.debug(f"解码异常堆栈: {traceback.format_exc()}")
            except Exception as e:
                self.logger.error(f"处理响应数据时出错: {str(e)}")
                if self.debug_mode:
                    import traceback
                    self.logger.debug(f"处理异常堆栈: {traceback.format_exc()}")
                
        except Exception as e:
            self.logger.error(f"处理响应时发生错误: {str(e)}")
            if self.debug_mode:
                import traceback
                self.logger.debug(f"异常堆栈: {traceback.format_exc()}")
    
    def _should_skip_response(self, response):
        """
        检查是否应该跳过该响应 - 与 wxlivespy 项目逻辑一致
        
        @param response: Response对象
        @return: 是否跳过
        """
        try:
            # 检查内容类型
            content_type = response.headers.get("content-type", "")
            if not content_type:
                self.logger.warning(f"没有内容类型的URL: {response.url}")
                return True
                
            # 移除字符集信息
            content_type = content_type.split(";")[0].strip()
            
            # 跳过媒体内容
            if any(media in content_type for media in ["video", "image", "audio"]):
                return True
                
            # 跳过特定内容类型
            exclude_list = [
                "text/css", 
                "application/javascript", 
                "application/x-javascript",
                "text/html",
                "text/javascript",
                "application/font-woff",
                "font/ttf",
                "text/plain"
            ]
            if content_type in exclude_list:
                return True
                
            # 检查 URL
            url = response.url
            
            # 只处理包含 mmfinderassistant-bin/live/msg 的请求
            if "mmfinderassistant-bin/live/msg" not in url:
                return True
                
            # 跳过特定 URL
            skip_urls = [
                "mmfinderassistant-bin/helper/hepler_merlin_mmdata",
                "mmfinderassistant-bin/live/finder_live_get_promote_info_list",
                "mmfinderassistant-bin/live/get_live_info"
            ]
            if any(skip_url in url for skip_url in skip_urls):
                return True
                
            return False
        except Exception as e:
            self.logger.error(f"检查是否跳过响应时出错: {str(e)}")
            return True
    
    def _on_console_message(self, msg):
        """
        控制台消息处理器 - 简化版
        
        @param msg: 控制台消息
        """
        # 过滤掉WebGL相关警告
        if any(webgl_term in msg.text for webgl_term in ["WebGL", "swiftshader", "SwiftShader"]):
            return
            
        # 处理其他类型的消息
        # if msg.type == "error":
        #     self.logger.error(f"控制台错误: {msg.text}")
        # elif msg.type == "warning":
        #     self.logger.warning(f"控制台警告: {msg.text}")
        # else:
        #     self.logger.info(f"控制台 {msg.type}: {msg.text}")

    async def _keep_browser_alive(self, page):
        """
        保持浏览器活动状态
        
        @param page: 页面对象
        """
        self.logger.info("启动浏览器保活任务")
        try:
            while self._keep_alive and not self._stop_event.is_set():
                try:
                    if page.is_closed():
                        self.logger.warning("页面已关闭，保活任务退出")
                        break
                    
                    # 执行一些简单的操作，保持页面活跃
                    await page.evaluate("() => { console.log('保活操作: ' + new Date().toISOString()); }")
                    
                    # 等待一段时间
                    await asyncio.sleep(10)  # 每10秒执行一次保活操作
                except Exception as e:
                    self.logger.warning(f"保活操作失败: {str(e)}")
                    await asyncio.sleep(5)  # 出错后等待短一些的时间
        except asyncio.CancelledError:
            self.logger.info("保活任务被取消")
        except Exception as e:
            self.logger.error(f"保活任务异常: {str(e)}")
        finally:
            self.logger.info("保活任务结束")

    async def _check_browser_alive(self):
        """
        检查浏览器是否存活
        """
        self.logger.info("启动浏览器存活检查任务")
        try:
            while not self._stop_event.is_set():
                try:
                    # 检查浏览器和上下文是否存在
                    if not self.browser or not self.context:
                        self.logger.warning("浏览器或上下文不存在，检查任务退出")
                        return False
                    
                    # 检查浏览器是否已关闭
                    try:
                        # 修复：is_connected() 可能不是异步方法，不需要 await
                        is_connected = self.browser.is_connected()
                        if not is_connected:
                            self.logger.warning("浏览器已断开连接，检查任务退出")
                            return False
                    except Exception as e:
                        self.logger.warning(f"检查浏览器连接状态时出错: {str(e)}")
                        return False
                    
                    # 检查上下文是否有页面
                    pages = self.context.pages
                    if not pages:
                        self.logger.warning("上下文没有页面，检查任务退出")
                        return False
                    
                    # 等待一段时间
                    await asyncio.sleep(2)  # 每2秒检查一次
                except Exception as e:
                    self.logger.error(f"检查浏览器存活状态时出错: {str(e)}")
                    return False
        except asyncio.CancelledError:
            self.logger.info("浏览器检查任务被取消")
            return True
        except Exception as e:
            self.logger.error(f"浏览器检查任务异常: {str(e)}")
            return False

    # 添加一个方法来处理自定义事件，用于测试
    async def handle_mock_response(self, page, mock_url, mock_request_data, mock_response_data):
        """
        处理模拟响应，用于测试
        
        @param page: 页面对象
        @param mock_url: 模拟URL
        @param mock_request_data: 模拟请求数据
        @param mock_response_data: 模拟响应数据
        """
        self.logger.info(f"处理模拟响应: {mock_url}")
        
        try:
            # 检查URL是否应该被处理
            if "mmfinderassistant-bin/live/msg" not in mock_url:
                self.logger.info(f"模拟URL不包含 mmfinderassistant-bin/live/msg，跳过处理: {mock_url}")
                return
                
            # 使用 WXDataDecoder 解码数据
            mock_headers = {"content-type": "application/json", "x-wechat-uin": "test_uin"}
            decoded_data = WXDataDecoder.decode_data_from_response(
                mock_headers, mock_request_data, mock_response_data
            )
            self.logger.info(f"解码数据: {decoded_data}")
            # 更新主播信息和直播信息
            # if decoded_data and "host_info" in decoded_data and decoded_data["host_info"]:
            #     self.logger.info(f"获取到主播信息: {decoded_data['host_info']}")
            
            # 更新直播信息
            if decoded_data and "live_info" in decoded_data and decoded_data["live_info"]:
                self.logger.info(f"获取到直播信息: {decoded_data['live_info']}")
                self.event_handler.on_status_update(decoded_data["live_info"])
            
            # 处理事件
            if decoded_data and "events" in decoded_data and len(decoded_data["events"]) > 0:
                self.logger.info(f"获取到事件: {decoded_data['events']}")
                self.event_handler.on_events({"events": decoded_data["events"]})
                
            self.logger.info(f"成功处理模拟响应: {mock_url}")
            
        except Exception as e:
            self.logger.error(f"处理模拟响应时出错: {str(e)}")
            import traceback
            self.logger.error(f"异常堆栈: {traceback.format_exc()}") 